、/********************************** (C) COPYRIGHT *******************************
* File Name          : oled库
* Author             : 未知
* Version            : 未知
* Date               : 未知
* Description        : 驱动屏幕使用的函数
*******************************************************************************/
#include <CH549_OLED.h>
#include <CH549_BMP.h>
#include <CH549_sdcc.h>	 
#include <CH549_DEBUG.h> 
#include <CH549_BMP.h>
#include <CH549_SPI.h>
#include <CH549_OLEDFONT.h> 
#define pgm_read_byte(addr)                                                    \
  (*(const u8 *)(addr)) ///< PROGMEM workaround for non-AVR
u8 fontSize;
void setFontSize(u8 size){
	fontSize = size;
}
/* OLED相关函数 */
//延迟函数
void delay_ms(unsigned int ms)
{                         
	unsigned int a;
	while(ms)
	{
		a=1800;
		while(a--);
		ms--;
	}
	return;
}

/*  向SSD1306写入一个字节。
    dat:要写入的数据/命令
    cmd:数据/命令标志 0,表示命令;1,表示数据; 
*/
void OLED_WR_Byte(u8 dat,u8 cmd)
{			
	 
	if(cmd)	OLED_MODE_DATA(); 	//命令模式
	else 	OLED_MODE_COMMAND(); 	//数据模式
	OLED_SELECT();			    //片选设置为0,设备选择
	CH549SPIMasterWrite(dat);       //使用CH549的官方函数写入8位数据
	OLED_DESELECT();			    //片选设置为1,取消设备选择
	OLED_MODE_DATA();   	  	    //转为数据模式
	
/* 	
	if(cmd)	OLED_DC_Set(); 	  //如果cmd=1，DC=1
	else 	OLED_DC_Clr(); 	  //如果cmd=0，DC=0

	OLED_CS_Clr();			  //片选设置为0
	
	CH549SPIMasterWrite(dat);  //写入8位数据

	OLED_CS_Set();			  //片选设置为1
	OLED_DC_Set();   	  	  //DC=1
	*/
} 

void load_one_command(u8 c){
	OLED_WR_Byte(c,OLED_CMD);
}

void load_commandList(const u8 *c, u8 n){
    while (n--) 
		OLED_WR_Byte(pgm_read_byte(c++),OLED_CMD);
      //SPIwrite(pgm_read_byte(c++));
}
/* OLED操作函数 */
//OLED初始化
void OLED_Init(void)
{
    //自我reset一下
    OLED_RST_Set();
	delay_ms(100);
	OLED_RST_Clr();
	delay_ms(100);
	OLED_RST_Set(); 
    //OLED的初始化指令，这里可以通过command数组让它变得更简洁，等会修改
	//目前这些指令大部分都已经改成已经定义好的名字了，但是也有一些并没有完全理解，等后面再看一下
  	static const u8 __code init_commandList[] = {	
		  								SSD1306_DISPLAYOFF,			    //--turn off oled panel	
                                    	//SSD1306_SETLOWCOLUMN,		    //---set low column address	
                                        //SSD1306_SETHIGHCOLUMN,		    //---set high column address	
										SSD1306_SETSTARTLINE,		    //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)	
										SSD1306_SETCONTRAST,		    //--set contrast control register	
										SSD1306_SET_SEG_CURRENT,	    // Set SEG Output Current Brightness	
										SSD1306_SET_SEG_MAP,		    //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常	
										SSD1306_COMSCANDEC,			    //Set COM/Row Scan Direction   0xc0上下反置 0xc8正常	
										SSD1306_NORMALDISPLAY,		    //--set normal display	
										SSD1306_SETMULTIPLEX,		    //--set multiplex ratio(1 to 64)	
										SSD1306_1_64_DUTY,			    //--1/64 duty	
										SSD1306_SETDISPLAYOFFSET,	    //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)	
										SSD1306_SETLOWCOLUMN,		    //-not offset	
										SSD1306_SETDISPLAYCLOCKDIV,	    //--set display clock divide ratio/oscillator frequency	
										SSD1306_100_DIVIDERATIO,	    //--set divide ratio, Set Clock as 100 Frames/Sec	
										SSD1306_SETPRECHARGE,		    //--set pre-charge period	
										SSD1306_SET_PRECHARGE,		    //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock	
										SSD1306_SETCOMPINS,			    //--set com pins hardware configuration	
										//SSD1306_unknown1,				//	
										SSD1306_SETVCOMDETECT,		    //--set vcomh	
										SSD1306_SETSTARTLINE,		    //Set VCOM Deselect Level	
										SSD1306_MEMORYMODE,			    //-Set Page Addressing Mode (0x00/0x01/0x02)	
										SSD1306_SWITCHCAPVCC,			//	
										SSD1306_CHARGEPUMP,			    //--set Charge Pump enable/disable	
										//SSD1306_0x10DISABLE,			//--set(0x10) disable	
										SSD1306_DISPLAYALLON_RESUME,    // Disable Entire Display On (0xa4/0xa5)	
										SSD1306_NORMALDISPLAY,		    // Disable Inverse Display On (0xa6/a7) 	
										SSD1306_DISPLAYON,			    //--turn on oled panel	
										    	
										SSD1306_DISPLAYON	            /*display ON*/ 	
                                      };

  	load_commandList(init_commandList, sizeof(init_commandList));

	OLED_Clear();
	OLED_Set_Pos(0,0); 	
}

//开启OLED显示  这里的代码待修改，完全移植的中景园
void OLED_Display_On(void)
{
	load_one_command(SSD1306_CHARGEPUMP);	//SET DCDC命令
	load_one_command(SSD1306_0x10DISABLE);	//DCDC ON
	load_one_command(SSD1306_DISPLAYON);	//DISPLAY ON
}

//关闭OLED显示     
void OLED_Display_Off(void)
{
	load_one_command(SSD1306_CHARGEPUMP);	//SET DCDC命令
	load_one_command(SSD1306_SETHIGHCOLUMN);	//DCDC OFF
	load_one_command(0XAE);	//DISPLAY OFF
}	

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!	  
void OLED_Clear(void)
{  
	u8 i,n;		    
	for(i=0;i<8;i++)  
	{  
		load_one_command(0xb0+i);	//设置页地址（0~7）
		load_one_command(SSD1306_SETLOWCOLUMN);		//设置显示位置—列低地址
		load_one_command(SSD1306_SETHIGHCOLUMN);		//设置显示位置—列高地址 

		for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 
	} //更新显示
}



/* OLED显示函数 */

//设置显示位置 (缺注释)
void OLED_Set_Pos(unsigned char col_index, unsigned char row_index) 
{ 
	load_one_command(0xb0+row_index);
	load_one_command(((col_index&0xf0)>>4)|SSD1306_SETHIGHCOLUMN);
	load_one_command((col_index&0x0f)|0x01);
}  

//这边showchar的原理是对输出字符的ascii码进行操作：'s'
//在指定位置上显示一个字符
//接下来要做的：暂时先不修改方法，修改变量名 & 简化思路 & 注释
void OLED_ShowChar(u8 col_index, u8 row_index, u8 chr)
{      	
	unsigned char char_index = 0, i = 0;	
		char_index = chr - ' ';	//将希望输入的字符的ascii码减去空格的ascii码，得到偏移后的值	因为在ascii码中space之前的字符并不能显示出来，字库里面不会有他们，减去一个空格相当于从空格往后开始数		
		
		if (col_index > Max_Column - 1) {
			col_index = 0;
			row_index = row_index + 2;
		} //这里是设定列溢出的规则，但是溢出之后y为什么+2而不是+1？

		if (fontSize == 16) {
			OLED_Set_Pos(col_index, row_index);	
			for (i = 0; i < 8; i++)
				OLED_WR_Byte(fontMatrix_8x16[char_index * 16 + i], OLED_DATA); //通过DATA模式写入矩阵数据就是在点亮特定的像素点
			OLED_Set_Pos(col_index, row_index + 1);
			for (i = 0; i < 8; i++)
				OLED_WR_Byte(fontMatrix_8x16[char_index * 16 + i + 8], OLED_DATA);
		}

		else {	
			OLED_Set_Pos(col_index, row_index + 1);
			for (i = 0; i < 6; i++)
				OLED_WR_Byte(fontMatrix_6x8[char_index][i], OLED_DATA);	
		}
}

void OLED_ShowString(u8 col_index, u8 row_index, u8 *chr)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(col_index,row_index,chr[j]);
			col_index+=8;
		if (col_index>120){col_index=0;row_index+=2;}
		j++;
	}
}


//显示汉字字符 目前用处比较小，因为没有中文库，每一个想要显示的中文都需要它在屏幕上匹配的坐标才能显示
void OLED_ShowCHinese(u8 col_index, u8 row_index, u8 no)
{      			    
	u8 t,adder=0;
	OLED_Set_Pos(col_index,row_index);	
    for(t=0;t<16;t++)
		{
				OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
				adder+=1;
     }	
		OLED_Set_Pos(col_index,row_index+1);	
    for(t=0;t<16;t++)
			{	
				OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
				adder+=1;
      }					
}




/***********功能描述：显示显示BMP图片128×64起始点坐标(x,y),x的范围0～127，y为页的范围0～7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
	//x0和y0是显示图片的初始坐标，一般为(0,0)
	//x1 = 128; y1 = 8
	unsigned int j = 0;
	unsigned char x,y;
  
	for(y = y0; y < y1; y++)	//这里的y和x是对二维数组行和列的两次for循环，遍历整个二维数组，并把每一位数据传给OLED
	
	{
		OLED_Set_Pos(x0,y);
    	for(x = x0; x < x1; x++)
	    {      
	    	OLED_WR_Byte(BMP[j++],OLED_DATA);	    	//向OLED输入BMP中 的一位数据，并逐次递增
	    }
	}
} 


